import socket
import threading
import thread
from netadapter import NetAdapter

##
# InterfaceServ
# Controla as interacoes entre os servidores
class InterfaceServ:

    ##
    # Construtor
    # Inicia as filas e o daemon UDP para envio de mensagem entre os servidores
    # @param servs lista dos servidores
    # @param idServ identificador do servidor 
    # @param clock relogio logico a ser utilizado
    def __init__( self, servs, idServ, clock ):

        # configurar serv
        self.servs = servs
        self.id = idServ
        self.ck = clock
        self.host = servs[idServ][1]
        self.port = int(servs[idServ][3])
        self.nbServs = len(servs)
        
        # configura UDP
        self.udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.orig = (self.host, self.port)
        self.udp.bind(self.orig)
        print "> servidor UDP rodando: " + self.host + ":" + str(self.port)

        # configurar lock's
        self.semFila = threading.Semaphore(0)
        self.mutexFila = []
        self.filas = []
        for i in range(0, self.nbServs):
            self.mutexFila.append(threading.Condition())
            self.filas.append([])
            
        # startar thread para recber msgs
        thread.start_new_thread(self.recebeMsg, tuple() )
    
    ##
    # recebeMsg
    # thread que recebe as msgs dos servidores e a enfileira
    def recebeMsg(self):
        while True:
            # recebe msg: timestamp#idRemetente#msg
            msg, serv = self.udp.recvfrom(1024)

            # gera novo clock
            inCk = msg.split("#")[0]
            ck = self.ck.newCk(inCk)

            # identifica o server remetente
            servId = msg.split("#")[1]

            # enfileira msg (retira clock e ident antes)
            msg = msg[msg.index("#")+1:]
            msg = msg[msg.index("#")+1:]
            self.pushElemento(ck,msg,servId)
    
    ##
    # pushElemento
    # guarda a mensagem msg com clock ck na fila do servidor servId
    # @param ck clock da mensagem
    # @param msg mensagem a ser enviada
    # @param servId id do servidor
    def pushElemento(self, ck, msg, servId):

        servId = int(servId)
        print "< servidor " + str(servId) + " postou msg: " + msg

        self.mutexFila[servId].acquire()
        self.filas[servId].append((ck,msg,servId))
        self.semFila.release()
        self.mutexFila[servId].release()
    
    ##
    # popElement
    # recupera a mensagem mais antiga
    # @return (ck, msg, servID) tupla contendo a mensagem msg com clock igual a ck do servidor servID
    def popElemento(self):

        # pega todos os locks
        self.semFila.acquire()
        for i in range(0, self.nbServs):
            self.mutexFila[i].acquire()

        # busca pelo menor clock
        ckRef = 9999999999 # numero "infinito" =P
        indFila = -1
        for i in range(0, self.nbServs):
            if (len(self.filas[i]) > 0):
                if (self.filas[i][0][0] < ckRef):
                    ckRef = self.filas[i][0][0]
                    indFila = i
    
        #se nao achou nenhuma msg nas filas responde nulo
        if (indFila == -1):
            #mensagem nula
            return (-1,"",-1)

        # pop na msg
        resp = self.filas[indFila].pop(0)

        # libera todos os locks
        for i in range(0, self.nbServs):
            self.mutexFila[i].release()

        # retorna a msg "popada"
        return resp
        
    ##
    # enviaMsg
    # envia a mensagem msg ao servidor idServ
    # @param msg msg a ser enviada ao servidor
    # @param idServ id do servidor (para encontrar os valores de host e port)
    def enviaMsg(self, msg, idServ):
        print '> enviando ', msg,' para ', idServ

        idServ = int(idServ)
        host = self.servs[idServ][1]
        port = int(self.servs[idServ][3])
        print '> host ', host,' port ', port

        udpTmp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        dest = (host, port)
        # envia: timestamp#idRemetente#msg
        udpTmp.sendto(str(self.ck.newCk(0)) + "#" + str(self.id) + "#" + msg, dest)
        udpTmp.close()





